package com.carb.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.carb.constant.AuthorityConstant;
import com.carb.constant.MessageConstant;
import com.carb.context.UserContext;
import com.carb.dto.*;
import com.carb.entity.*;
import com.carb.enums.CommonDeleted;
import com.carb.enums.CommonStatus;
import com.carb.enums.ItemOperationType;
import com.carb.exception.BusinessErrorException;
import com.carb.exception.DeletionNotAllowedException;
import com.carb.exception.EntityNotFoundException;
import com.carb.exception.UpdateStatusFailedException;
import com.carb.mapper.*;
import com.carb.query.PageQueryUtils;
import com.carb.result.PageResult;
import com.carb.service.IItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.carb.vo.ItemStockRecordVO;
import com.carb.vo.ItemVO;
import com.carb.websocket.WebSocketServer;
import jakarta.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 物品管理 服务实现类
 * </p>
 *
 * @author XuChaoyang
 * @since 2025-01-18
 */
@Service
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements IItemService {

    @Resource
    private ProjectItemMapper projectItemMapper;

    @Resource
    private ItemCategoryMapper itemCategoryMapper;

    @Resource
    private AnnouncementMapper announcementMapper;

    @Resource
    private ItemStockRecordMapper itemStockRecordMapper;

    @Resource
    private ProjectMapper projectMapper;

    @Resource
    private WebSocketServer webSocketServer;

    /**
     * 新增物品
     * @param itemAddDTO 新增物品信息
     */
    @Override
    @Transactional
    public void addItem(ItemAddDTO itemAddDTO) {
        // 1、检查物品类别是否存在
        checkItemCategoryExist(itemAddDTO.getItemCategoryId());

        // 2、检查物品名称是否已经存在且逻辑删除（已删除）
        Item isItemExist = this.getOne(Wrappers.lambdaQuery(Item.class)
                .eq(Item::getName, itemAddDTO.getName())
                .eq(Item::getDeleted, CommonDeleted.DELETED));
        if (isItemExist != null) {
            // 物品id
            Long itemId = isItemExist.getId();
            // 更新物品逻辑删除为未删除
            BeanUtils.copyProperties(itemAddDTO, isItemExist);
            isItemExist.setDeleted(CommonDeleted.NOT_DELETED)
                    .setCreateTime(LocalDateTime.now())
                    .setUpdateTime(LocalDateTime.now())
                    .setStock(0);
            this.update(isItemExist, Wrappers.lambdaUpdate(Item.class)
                    .eq(Item::getId, itemId));
            // 新增库存预警公告
            Announcement announcement = new Announcement()
                    .setTitle(MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                    .setRelatedId(itemId)
                    .setContent(isItemExist.getName() + MessageConstant.ANN_ITEM_STOCK_LE_MINSTOCK);
            announcementMapper.insert(announcement);
            webSocketServer.sendToAllClients(JSON.toJSONString(announcement));
            // 跳过新增逻辑
            return;
        }

        // 3、物品属性拷贝
        Item item = new Item();
        BeanUtils.copyProperties(itemAddDTO, item);

        // 4、填充物品属性
        // 4.1、设置默认物品状态，默认为禁用状态
        item.setStatus(CommonStatus.DISABLED).setStock(0);

        // 5、新增物品，并得到物品id（save后主键会自动set到实体的id字段，只需要getId()就好）
        this.save(item);

        // 6、同时新增库存预警公告
        Announcement announcement = new Announcement()
                .setTitle(MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                .setRelatedId(item.getId())
                .setContent(item.getName() + MessageConstant.ANN_ITEM_STOCK_LE_MINSTOCK);
        announcementMapper.insert(announcement);
        webSocketServer.sendToAllClients(JSON.toJSONString(announcement));
    }

    /**
     * 物品分页查询
     * @param itemPageQueryDTO 查询条件
     * @return 物品分页结果
     */
    @Override
    public PageResult<ItemVO> pageQuery(ItemPageQueryDTO itemPageQueryDTO) {
        // 1、获取查询属性
        // 1.1、分页属性
        Long pageNum = PageQueryUtils.checkPageNum(itemPageQueryDTO.getPageNum());
        Long pageSize = PageQueryUtils.checkPageSize(itemPageQueryDTO.getPageSize());
        String orderBy = itemPageQueryDTO.getOrderBy();
        Boolean isAsc = itemPageQueryDTO.getIsAsc();
        // 1.2、查询属性
        String name = itemPageQueryDTO.getName();
        Long itemCategoryId = itemPageQueryDTO.getItemCategoryId();
        String brand = itemPageQueryDTO.getBrand();
        CommonStatus status = itemPageQueryDTO.getStatus();

        // 2、构建条件
        // 2.1、分页条件
        Page<Item> page = Page.of(pageNum, pageSize);
        // 2.2、排序条件
        PageQueryUtils.applySorting(page, orderBy, isAsc);

        // 3、物品表查询（物品名称（非空）、物品类别（非空）、品牌（非空）、物品状态（非空）、逻辑删除（未删除））
        Page<Item> itemPage = this.page(page, Wrappers.lambdaQuery(Item.class)
                .like(name != null, Item::getName, name)
                .eq(itemCategoryId != null, Item::getItemCategoryId, itemCategoryId)
                .like(brand != null, Item::getBrand, brand)
                .eq(status != null, Item::getStatus, status)
                .eq(Item::getDeleted, CommonDeleted.NOT_DELETED));

        // 4、封装VO数据列表
        List<ItemVO> itemVOList = new ArrayList<>();
        for (Item item : itemPage.getRecords()) {
            ItemVO itemVO = new ItemVO();
            BeanUtils.copyProperties(item, itemVO);
            // 物品类别名称
            String itemCategoryName = itemCategoryMapper.selectById(item.getItemCategoryId()).getName();
            itemVO.setItemCategoryName(itemCategoryName);
            itemVOList.add(itemVO);
        }

        // 5、将实体分页对象转换为VO分页对象
        Page<ItemVO> itemVOPage = PageQueryUtils.convertToVOPage(itemPage, itemVOList);

        // 6、返回封装结果
        return new PageResult<>(itemVOPage.getTotal(), itemVOPage.getPages(), itemVOPage.getRecords());
    }

    /**
     * 启用/禁用物品状态
     * @param status 物品状态
     * @param id 物品id
     */
    @Override
    @Transactional
    public void updateStatus(Long id, CommonStatus status) {
        // 1、检查物品是否存在
        Item item = checkItemExist(id);

        // 2、检查物品类别是否存在、是否禁用
        ItemCategory itemCategory = checkItemCategoryExist(item.getItemCategoryId());
        if (itemCategory.getStatus() == CommonStatus.DISABLED) {
            // 物品类别已禁用，状态更新失败
            throw new UpdateStatusFailedException(MessageConstant.ITEM_CATEGORY_DISABLED + MessageConstant.STATUS_UPDATE_FAILED);
        }

        // 3、启用物品，需要检查物品是否大于最小库存
        if (status == CommonStatus.ENABLED && item.getStock() <= item.getMinStock()) {
            // 物品库存不足
            throw new UpdateStatusFailedException(item.getName() + MessageConstant.ITEM_STOCK_NOT_ENOUGH + MessageConstant.STATUS_UPDATE_FAILED);
        }

        // 4、禁用物品，需要同时禁用所有与之关联的服务项目
        if (status == CommonStatus.DISABLED) {
            disableRelatedProjectByItemIds(List.of(id));
        }

        // 5、更新
        this.update(new Item(), Wrappers.lambdaUpdate(Item.class)
                .eq(Item::getId, id)
                .set(Item::getStatus, status));
    }

    /**
     * 根据物品id查询物品信息
     * @param id 物品id
     * @return 查询结果
     */
    @Override
    public ItemVO getItemById(Long id) {
        // 1、检查物品是否存在
        Item item = checkItemExist(id);

        // 2、封装VO
        ItemVO itemVO = new ItemVO();
        BeanUtils.copyProperties(item, itemVO);
        // 物品类别名称
        String itemCategoryName = itemCategoryMapper.selectById(item.getItemCategoryId()).getName();
        itemVO.setItemCategoryName(itemCategoryName);

        // 3、返回VO
        return itemVO;
    }

    /**
     * 修改物品信息
     * @param itemUpdateDTO 修改的物品信息
     */
    @Override
    @Transactional
    public void updateItem(ItemUpdateDTO itemUpdateDTO) {
        // 1、检查物品是否存在
        Long itemId = itemUpdateDTO.getId();
        Item item = checkItemExist(itemId);

        // 2、检查物品类别是否存在
        checkItemCategoryExist(itemUpdateDTO.getItemCategoryId());

        // 3、物品更新信息
        String name = itemUpdateDTO.getName();
        Long itemCategoryId = itemUpdateDTO.getItemCategoryId();
        String brand = itemUpdateDTO.getBrand();
        Integer price = itemUpdateDTO.getPrice();
        Integer minStock = itemUpdateDTO.getMinStock();
        String description = itemUpdateDTO.getDescription();
        String picUrl = itemUpdateDTO.getPicUrl();

        // 4、判断状态是否需要更新，以及是否需要删除库存预警公告
        Integer stock = item.getStock();
        CommonStatus status = item.getStatus();
        boolean needDeleteAnnouncement = false; // 是否需要删除库存预警公告
        boolean needInsertAnnouncement = false; // 是否需要新增库存预警公告
        // 如果修改了最小库存，则需要检查库存是否大于修改后最小库存
        if (!minStock.equals(item.getMinStock())) {
            if (stock > minStock && status == CommonStatus.DISABLED) {
                // 库存>最小库存，且原始状态为禁用，则需要启用物品
                status = CommonStatus.ENABLED;
                // 需要删除库存预警公告
                needDeleteAnnouncement = true;
            } else if ( stock <= minStock && status == CommonStatus.ENABLED) {
                // 库存≤最小库存，且原始状态为启用，则需要禁用物品
                status = CommonStatus.DISABLED;
                // 同时禁用所有与之关联的服务项目
                disableRelatedProjectByItemIds(List.of(itemId));
                // 需要新增库存预警公告
                needInsertAnnouncement = true;
            }
        }

        // 5、物品更新
        this.update(new Item(), Wrappers.lambdaUpdate(Item.class)
                .eq(Item::getId, itemId)
                .set(Item::getName, name)
                .set(Item::getItemCategoryId, itemCategoryId)
                .set(Item::getBrand, brand)
                .set(Item::getPrice, price)
                .set(Item::getMinStock, minStock)
                .set(Item::getDescription, description)
                .set(Item::getPicUrl, picUrl)
                .set(Item::getStatus, status));

        // 6、是否需要删除库存预警公告
        if (needDeleteAnnouncement) {
            announcementMapper.delete(Wrappers.lambdaQuery(Announcement.class)
                    .eq(Announcement::getTitle, MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                    .eq(Announcement::getRelatedId, itemId));
            return; // 删除公告后直接返回
        }

        // 7、是否需要新增库存预警公告
        if (needInsertAnnouncement) {
            Announcement announcement = new Announcement()
                    .setTitle(MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                    .setRelatedId(itemId)
                    .setContent(name + MessageConstant.ANN_ITEM_STOCK_LE_MINSTOCK);
            announcementMapper.insert(announcement);
            webSocketServer.sendToAllClients(JSON.toJSONString(announcement));
            return; // 新增公告后直接返回
        }

        // 8、如果修改了物品名称，需要更新库存预警公告内容
        if (!name.equals(item.getName())) {
            announcementMapper.update(new Announcement(), Wrappers.lambdaUpdate(Announcement.class)
                    .eq(Announcement::getTitle, MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                    .eq(Announcement::getRelatedId, itemId)
                    .set(Announcement::getContent, name + MessageConstant.ANN_ITEM_STOCK_LE_MINSTOCK));
        }
    }

    /**
     * 删除物品（逻辑删除）
     * @param id 物品id
     */
    @Override
    @Transactional
    public void deleteItem(Long id) {
        // 1、检查物品是否存在
        Item item = checkItemExist(id);

        // 2、检查物品是否有库存
        if (item.getStock() != 0) {
            // 物品库存不为0，禁止删除
            throw new DeletionNotAllowedException(MessageConstant.ITEM_STOCK_NOT_EMPTY + MessageConstant.CAN_NOT_DELETE);
        }

        // 3、删除物品前判断是否关联有服务项目
        long count = projectItemMapper.selectCount(Wrappers.lambdaQuery(ProjectItem.class)
                .eq(ProjectItem::getItemId, id));
        if (count > 0) {
            // 物品已关联服务项目，禁止删除
            throw new DeletionNotAllowedException(MessageConstant.ITEM_BE_RELATED_BY_PROJECT + MessageConstant.CAN_NOT_DELETE);
        }

        // 4、删除物品
        this.update(new Item(), Wrappers.lambdaUpdate(Item.class)
                .eq(Item::getId, id)
                .set(Item::getDeleted, CommonDeleted.DELETED));

        // 5、删除公告
        announcementMapper.delete(Wrappers.lambdaQuery(Announcement.class)
                .eq(Announcement::getTitle, MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                .eq(Announcement::getRelatedId, id));
    }

    /**
     * 新增物品类别
     * @param itemCategoryName 新增物品类别名称
     */
    @Override
    @Transactional
    public void addItemCategory(String itemCategoryName) {
        // 1、检查物品类别是否已经存在且逻辑删除（已删除）
        ItemCategory isItemCategoryExist = itemCategoryMapper.selectOne(Wrappers.lambdaQuery(ItemCategory.class)
                .eq(ItemCategory::getName, itemCategoryName)
                .eq(ItemCategory::getDeleted, CommonDeleted.DELETED));
        if (isItemCategoryExist != null) {
            // 物品类别id
            Long itemCategoryId = isItemCategoryExist.getId();
            // 更新物品类别逻辑删除为未删除
            isItemCategoryExist.setDeleted(CommonDeleted.NOT_DELETED)
                    .setCreateTime(LocalDateTime.now())
                    .setUpdateTime(LocalDateTime.now());
            itemCategoryMapper.update(isItemCategoryExist, Wrappers.lambdaUpdate(ItemCategory.class)
                    .eq(ItemCategory::getId, itemCategoryId));
            // 跳过新增逻辑
            return;
        }

        // 2、新增物品类别
        itemCategoryMapper.insert(new ItemCategory()
                .setName(itemCategoryName)
                .setStatus(CommonStatus.ENABLED));
    }

    /**
     * 物品类别分页查询
     * @param itemCategoryPageQueryDTO 查询条件
     * @return 物品类别分页结果
     */
    @Override
    public PageResult<ItemCategory> pageQueryItemCategory(ItemCategoryPageQueryDTO itemCategoryPageQueryDTO) {
        // 1、获取查询属性
        // 1.1、分页属性
        Long pageNum = PageQueryUtils.checkPageNum(itemCategoryPageQueryDTO.getPageNum());
        Long pageSize = PageQueryUtils.checkPageSize(itemCategoryPageQueryDTO.getPageSize());
        String orderBy = itemCategoryPageQueryDTO.getOrderBy();
        Boolean isAsc = itemCategoryPageQueryDTO.getIsAsc();
        // 1.2、查询属性
        String itemCategoryName = itemCategoryPageQueryDTO.getItemCategoryName();
        CommonStatus status = itemCategoryPageQueryDTO.getStatus();

        // 2、构建条件
        // 2.1、分页条件
        Page<ItemCategory> page = Page.of(pageNum, pageSize);
        // 2.2、排序条件
        PageQueryUtils.applySorting(page, orderBy, isAsc);

        // 3、物品类别表查询
        Page<ItemCategory> itemCategoryPage = itemCategoryMapper.selectPage(page, Wrappers.lambdaQuery(ItemCategory.class)
                .like(itemCategoryName != null, ItemCategory::getName, itemCategoryName)
                .eq(status != null, ItemCategory::getStatus, status)
                .eq(ItemCategory::getDeleted, CommonDeleted.NOT_DELETED));

        // 4、返回封装结果
        return new PageResult<>(itemCategoryPage.getTotal(), itemCategoryPage.getPages(), itemCategoryPage.getRecords());
    }

    /**
     * 启用/禁用物品类别状态
     * @param status 物品类别状态
     * @param id 物品类别id
     */
    @Override
    @Transactional
    public void updateItemCategoryStatus(Long id, CommonStatus status) {
        // 1、检查物品类别是否存在
        checkItemCategoryExist(id);

        // 2、若是禁用，则需要禁用所有与之关联的物品，并且同时禁用物品关联的服务项目
        if (status == CommonStatus.DISABLED) {
            // 获取该物品类别下的所有物品id
            List<Long> itemIds = this.list(Wrappers.lambdaQuery(Item.class)
                    .eq(Item::getItemCategoryId, id)
                    .select(Item::getId))
                    .stream()
                    .map(Item::getId)
                    .toList();
            if (!itemIds.isEmpty()) {
                // 禁用所有与之关联的物品
                this.update(new Item(), Wrappers.lambdaUpdate(Item.class)
                        .in(Item::getId, itemIds)
                        .set(Item::getStatus, CommonStatus.DISABLED));
                // 禁用物品关联的服务项目
                disableRelatedProjectByItemIds(itemIds);
            }
        }

        // 2、更新
        itemCategoryMapper.update(new ItemCategory(), Wrappers.lambdaUpdate(ItemCategory.class)
                .eq(ItemCategory::getId, id)
                .set(ItemCategory::getStatus, status));
    }

    /**
     * 根据物品类别id查询物品类别信息
     * @param id 物品类别id
     * @return 查询结果
     */
    @Override
    public ItemCategory getItemCategoryById(Long id) {
        // 1、检查物品类别是否存在，并返回
        return checkItemCategoryExist(id);
    }

    /**
     * 修改物品类别信息
     * @param itemCategoryUpdateDTO 修改的物品类别信息
     */
    @Override
    @Transactional
    public void updateItemCategory(ItemCategoryUpdateDTO itemCategoryUpdateDTO) {
        // 1、获取参数
        Long id = itemCategoryUpdateDTO.getId();
        String itemCategoryName = itemCategoryUpdateDTO.getItemCategoryName();

        // 2、检查物品类别是否存在
        checkItemCategoryExist(id);

        // 3、更新
        itemCategoryMapper.update(new ItemCategory(), Wrappers.lambdaUpdate(ItemCategory.class)
                .eq(ItemCategory::getId, id)
                .set(ItemCategory::getName, itemCategoryName));
    }

    /**
     * 删除物品类别（逻辑删除）
     * @param id 物品类别id
     */
    @Override
    @Transactional
    public void deleteItemCategory(Long id) {
        // 1、检查物品类别是否存在
        checkItemCategoryExist(id);

        // 2、检查物品类别是否关联有物品（未删除）
        long count = this.count(Wrappers.lambdaQuery(Item.class)
                .eq(Item::getItemCategoryId, id)
                .eq(Item::getDeleted, CommonDeleted.NOT_DELETED));
        if (count > 0) {
            // 物品类别关联有物品
            throw new DeletionNotAllowedException(MessageConstant.ITEM_CATEGORY_BE_RELATED_BY_ITEM + MessageConstant.CAN_NOT_DELETE);
        }

        // 3、删除物品类别（逻辑删除）
        itemCategoryMapper.update(new ItemCategory(), Wrappers.lambdaUpdate(ItemCategory.class)
                .eq(ItemCategory::getId, id)
                .set(ItemCategory::getDeleted, CommonDeleted.DELETED));
    }

    /**
     * 查询所有启用的物品类别
     * @return 启用的物品类别列表
     */
    @Override
    @Transactional
    public List<ItemCategory> listItemCategory() {
        return itemCategoryMapper.selectList(Wrappers.lambdaQuery(ItemCategory.class)
                .eq(ItemCategory::getStatus, CommonStatus.ENABLED)
                .eq(ItemCategory::getDeleted, CommonDeleted.NOT_DELETED));
    }

    /**
     * 物品入库
     * @param itemStockUpdateDTOList 物品入库信息列表
     */
    @Override
    @Transactional
    public void addItemStock(List<ItemStockUpdateDTO> itemStockUpdateDTOList) {
        // 1、物品库存记录列表
        List<ItemStockRecord> itemStockRecordList = new ArrayList<>();

        // 2、遍历物品入库信息列表
        for (ItemStockUpdateDTO itemStockUpdateDTO : itemStockUpdateDTOList) {
            // 2.1、检查物品是否存在
            Long id = itemStockUpdateDTO.getId();
            Integer quantity = itemStockUpdateDTO.getQuantity();
            String note = StringUtils.isBlank(itemStockUpdateDTO.getNote()) ? MessageConstant.ITEM_STOCK_IN_ADJUST : itemStockUpdateDTO.getNote();
            Item item = checkItemExist(id);
            // 2.2、如果数量小于等于0，则跳过此物品入库
            if (quantity <= 0) continue;
            // 2.3、添加物品入库记录
            itemStockRecordList.add(new ItemStockRecord()
                    .setItemId(id)
                    .setOperationType(ItemOperationType.IN)
                    .setQuantity(quantity)
                    .setNote(note)
                    .setOperatorUserId(UserContext.getUserId()));
            // 2.4、添加物品库存数量
            Integer newStock = item.getStock() + quantity;
            CommonStatus status = item.getStatus();
            updateItemStock(newStock, item, status, true);
        }

        // 3、批量插入物品入库记录
        itemStockRecordMapper.insert(itemStockRecordList);
    }

    /**
     * 物品出库
     * @param itemStockUpdateDTOList 物品出库信息列表
     */
    @Override
    @Transactional
    public void reduceItemStock(List<ItemStockUpdateDTO> itemStockUpdateDTOList) {
        // 1、物品库存记录列表
        List<ItemStockRecord> itemStockRecordList = new ArrayList<>();

        // 2、遍历物品出库信息列表
        for (ItemStockUpdateDTO itemStockUpdateDTO : itemStockUpdateDTOList) {
            // 2.1、检查物品是否存在
            Long id = itemStockUpdateDTO.getId();
            Integer quantity = itemStockUpdateDTO.getQuantity();
            String note = StringUtils.isBlank(itemStockUpdateDTO.getNote()) ? MessageConstant.ITEM_STOCK_OUT_ADJUST : itemStockUpdateDTO.getNote();
            Item item = checkItemExist(id);
            // 2.2、如果数量小于等于0，则跳过此物品出库
            if (quantity <= 0) continue;
            // 2.3、检查物品库存是否禁用，是否足以抵扣出库数量
            Integer stock = item.getStock();
            int newStock = stock - quantity;
            CommonStatus status = item.getStatus();
            if (status == CommonStatus.DISABLED && !canIgnoreDisable()) {
                // 物品已禁用，当前角色不允许忽略物品禁用
                throw new BusinessErrorException(MessageConstant.ITEM_DISABLED);
            }
            if (newStock < 0) {
                // 库存不足
                throw new BusinessErrorException(MessageConstant.ITEM_STOCK_NOT_ENOUGH);
            }
            // 2.4、添加物品出库记录
            itemStockRecordList.add(new ItemStockRecord()
                    .setItemId(id)
                    .setOperationType(ItemOperationType.OUT)
                    .setQuantity(quantity)
                    .setNote(note)
                    .setOperatorUserId(UserContext.getUserId()));

            // 2.5、扣减物品库存数量
            updateItemStock(newStock, item, status, false);
        }

        // 3、批量插入物品出库记录
        itemStockRecordMapper.insert(itemStockRecordList);
    }

    /**
     * 物品库存记录分页查询
     * @param itemStockRecordPageQueryDTO 查询条件
     * @return 物品库存记录分页结果
     */
    @Override
    public PageResult<ItemStockRecordVO> pageQueryItemStockRecord(ItemStockRecordPageQueryDTO itemStockRecordPageQueryDTO) {
        // 1、获取查询属性
        // 1.1、分页属性
        Long pageNum = PageQueryUtils.checkPageNum(itemStockRecordPageQueryDTO.getPageNum());
        Long pageSize = PageQueryUtils.checkPageSize(itemStockRecordPageQueryDTO.getPageSize());
        String orderBy = itemStockRecordPageQueryDTO.getOrderBy();
        Boolean isAsc = itemStockRecordPageQueryDTO.getIsAsc();
        // 1.2、查询属性
        String itemName = itemStockRecordPageQueryDTO.getItemName();
        ItemOperationType operationType = itemStockRecordPageQueryDTO.getOperationType();
        String note = itemStockRecordPageQueryDTO.getNote();

        // 2、构造条件
        // 2.1、分页条件
        Page<ItemStockRecord> page = Page.of(pageNum, pageSize);
        // 2.2、排序条件
        PageQueryUtils.applySorting(page, orderBy, isAsc);
        // 2.3、空结果
        PageResult<ItemStockRecordVO> emptyPageResult = new PageResult<>(page.getTotal(), page.getPages(), Collections.emptyList());


        // 3、获取需要查询的物品id列表（物品名称（非空））
        List<Item> itemList = this.list(Wrappers.lambdaQuery(Item.class)
                .like(itemName != null, Item::getName, itemName));
        if (itemList.isEmpty()) return emptyPageResult;

        // 4、构造物品id与物品名称的映射关系
        Map<Long, String> itemMap = new HashMap<>();
        List<Long> itemIds = new ArrayList<>();
        for (Item item : itemList) {
            itemIds.add(item.getId());
            itemMap.put(item.getId(), item.getName());
        }

        // 5、物品库存记录查询（物品id、操作类型（非空）、备注（非空））
        Page<ItemStockRecord> itemStockRecordPage = itemStockRecordMapper.selectPage(page, Wrappers.lambdaQuery(ItemStockRecord.class)
                .in(ItemStockRecord::getItemId, itemIds)
                .eq(operationType != null, ItemStockRecord::getOperationType, operationType)
                .like(note != null, ItemStockRecord::getNote, note));

        // 6、封装VO数据列表
        List<ItemStockRecordVO> itemStockRecordVOList = new ArrayList<>();
        for (ItemStockRecord itemStockRecord : itemStockRecordPage.getRecords()) {
            ItemStockRecordVO itemStockRecordVO = new ItemStockRecordVO();
            BeanUtils.copyProperties(itemStockRecord, itemStockRecordVO);
            itemStockRecordVO.setItemName(itemMap.get(itemStockRecord.getItemId()));
            itemStockRecordVOList.add(itemStockRecordVO);
        }

        // 7、将实体分页对象转换为VO分页对象
        Page<ItemStockRecordVO> itemStockRecordVOPage = PageQueryUtils.convertToVOPage(itemStockRecordPage, itemStockRecordVOList);

        // 8、返回封装结果
        return new PageResult<>(itemStockRecordVOPage.getTotal(), itemStockRecordVOPage.getPages(), itemStockRecordVOList);
    }

    /**
     * 更新物品库存数量
     * @param newStock 新的库存数量
     * @param item 物品对象
     * @param status 物品状态
     */
    private void updateItemStock(Integer newStock, Item item, CommonStatus status, Boolean isAdd) {
        // 1、（入库）新库存数量>最小库存，则更新物品状态为启用，并删除库存预警公告（原状态为禁用才执行）
        if (isAdd && newStock > item.getMinStock()) {
            status = CommonStatus.ENABLED;
            if (item.getStatus() == CommonStatus.DISABLED) {
                announcementMapper.delete(Wrappers.lambdaQuery(Announcement.class)
                        .eq(Announcement::getTitle, MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                        .eq(Announcement::getRelatedId, item.getId()));
            }
        }
        // 2、（出库）新库存数量≤最小库存数量，则更新物品状态为禁用，同时禁用与之关联的服务项目，同时新增库存预警公告
        if (!isAdd && newStock <= item.getMinStock()) {
            // 则更新物品状态为禁用
            status = CommonStatus.DISABLED;
            // 禁用与之关联的服务项目
            disableRelatedProjectByItemIds(List.of(item.getId()));
            // 新增库存预警公告
            Announcement announcement = new Announcement()
                    .setTitle(MessageConstant.ITEM_STOCK_NOT_ENOUGH)
                    .setRelatedId(item.getId())
                    .setContent(item.getName() + MessageConstant.ANN_ITEM_STOCK_LE_MINSTOCK);
            announcementMapper.insert(announcement);
            webSocketServer.sendToAllClients(JSON.toJSONString(announcement));
        }
        // 更新库存数量
        boolean update = this.update(new Item(), Wrappers.lambdaUpdate(Item.class)
                .eq(Item::getId, item.getId())
                .eq(Item::getStock, item.getStock()) // 乐观锁CAS
                .set(Item::getStock, newStock)
                .set(Item::getStatus, status));
        if (!update) {
            // 并发修改
            throw new BusinessErrorException(MessageConstant.CONCURRENT_MODIFICATION);
        }
    }

    /**
     * 禁用与物品相关的的服务项目
     * @param itemIds 物品id集合
     */
    private void disableRelatedProjectByItemIds(List<Long> itemIds) {
        Set<Long> projectIds = projectItemMapper.selectList(Wrappers.lambdaQuery(ProjectItem.class)
                .in(ProjectItem::getItemId, itemIds))
                .stream()
                .map(ProjectItem::getProjectId)
                .collect(Collectors.toSet());
        if (!projectIds.isEmpty()) {
            projectMapper.update(new Project(), Wrappers.lambdaUpdate(Project.class)
                    .in(Project::getId, projectIds)
                    .set(Project::getStatus, CommonStatus.DISABLED));
        }
    }

    /**
     * 检查物品是否存在
     * @param itemId 物品id
     * @return 物品对象
     */
    private Item checkItemExist(Long itemId) {
        Item item = this.getOne(Wrappers.lambdaQuery(Item.class)
                .eq(Item::getId, itemId)
                .eq(Item::getDeleted, CommonDeleted.NOT_DELETED));
        if (item == null) {
            // 物品不存在，抛出异常
            throw new EntityNotFoundException(MessageConstant.ITEM_NOT_FOUND);
        }
        return item;
    }

    /**
     * 检查物品类别是否存在
     * @param itemCategoryId 物品类别id
     * @return 物品类别对象
     */
    private ItemCategory checkItemCategoryExist(Long itemCategoryId) {
        ItemCategory itemCategory = itemCategoryMapper.selectOne(Wrappers.lambdaQuery(ItemCategory.class)
                .eq(ItemCategory::getId, itemCategoryId)
                .eq(ItemCategory::getDeleted, CommonDeleted.NOT_DELETED));
        if (itemCategory == null) {
            // 物品类别不存在，抛出异常
            throw new EntityNotFoundException(MessageConstant.ITEM_CATEGORY_NOT_FOUND);
        }
        return itemCategory;
    }

    /**
     * 判断当前用户角色是否可以忽略物品禁用
     * @return true: 可以忽略物品禁用，false: 不可以忽略物品禁用
     */
    private boolean canIgnoreDisable() {
        // 1、获取当前用户角色
        String userRole = UserContext.getUserRole();

        // 2、可以忽略禁用的权限
        List<String> requiredRoles = new ArrayList<>() {{
            add(AuthorityConstant.ADMIN);
            add(AuthorityConstant.STORE_MANAGER);
            add(AuthorityConstant.PURCHASER);
        }};

        // 3、包含当前用户角色，则返回true
        return requiredRoles.contains(userRole);
    }

}
